﻿//*****************************-》 基类 循环列表 《-****************************
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using UnityEngine.EventSystems;
using System;

namespace CircularScrollView
{
    public enum e_Direction
    {
        Horizontal,
        Vertical
    }
    
    public class UICircularScrollView : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler
    {
        #region 箭头
        
        public bool isShowArrow = false;
        public GameObject pointingFirstArrow;
        public GameObject pointingEndArrow;

        #endregion
        
        public e_Direction direction = e_Direction.Horizontal;
        public int row = 1;
        public float spacing = 0f; //间距
        public float spacingVer = -1f;//垂直于列表方向的space
        public ScrollViewCell scrollViewCell; //指定的cell

        protected Action<ScrollViewCell, int> FuncCallBack;
        protected Action<ScrollViewCell, int> FuncOnClickCallBack;
        
        protected RectTransform m_ScrollRectTrans;
        
        protected float m_PlaneWidth;
        protected float m_PlaneHeight;

        protected float m_ContentWidth;
        protected float m_ContentHeight;

        protected float m_CellObjectWidth;
        protected float m_CellObjectHeight;
        protected Vector2 m_CellOriginPos;

        protected GameObject m_Content;
        protected RectTransform m_ContentRectTrans;

        private bool m_isInited = false;

        //记录 物体的坐标 和 物体 
        protected struct CellInfo
        {
            public Vector2 pos;
            public ScrollViewCell obj;
        };
        protected CellInfo[] m_CellInfos;

        protected bool m_isSetCount = false;

        protected ScrollRect m_ScrollRect;

        protected int m_MaxCount = -1; //列表数量

        protected int m_MinIndex = -1;
        protected int m_MaxIndex = -1;

        protected bool m_IsClearList = false; //是否清空列表

        public virtual void Init(Action<ScrollViewCell, int> callBack)
        {
            Init(callBack, null);
        }
        public virtual void Init(Action<ScrollViewCell, int> callBack,int maxCount)
        {
            Init(callBack, null);
            ShowList(maxCount);
        }
        public virtual void Init(Action<ScrollViewCell, int> callBack, Action<ScrollViewCell, int> onClickCallBack)
        {

            DisposeAll();

            FuncCallBack = callBack;

            if (onClickCallBack != null)
            {
                FuncOnClickCallBack = onClickCallBack;
            }

            if (m_isInited)
                return;

            m_ScrollRect = this.GetComponent<ScrollRect>();
            
            m_Content = m_ScrollRect.content.gameObject;

            if (scrollViewCell == null)
            {
                scrollViewCell = m_Content.transform.GetChild(0).GetComponent<ScrollViewCell>();
            }
            SetPoolsObj(scrollViewCell);

            RectTransform cellRectTrans = scrollViewCell.transform.GetComponent<RectTransform>();
            cellRectTrans.pivot = new Vector2(0f, 1f);
            CheckAnchor(cellRectTrans);
          
            //记录 Cell 信息
            m_CellObjectHeight = cellRectTrans.rect.height;
            m_CellObjectWidth = cellRectTrans.rect.width;
            m_CellOriginPos = cellRectTrans.anchoredPosition;

            //记录 Plane 信息
            m_ScrollRectTrans = GetComponent<RectTransform>();
            Rect planeRect = m_ScrollRectTrans.rect;
            m_PlaneHeight = planeRect.height;
            m_PlaneWidth = planeRect.width;

            //记录 Content 信息
            m_ContentRectTrans = m_Content.GetComponent<RectTransform>();
            Rect contentRect = m_ContentRectTrans.rect;
            m_ContentHeight = contentRect.height;
            m_ContentWidth = contentRect.width;

            m_ContentRectTrans.pivot = new Vector2(0f, 1f);
            CheckAnchor(m_ContentRectTrans);
            
            m_ScrollRect.onValueChanged.RemoveAllListeners();
            //添加滑动事件
            m_ScrollRect.onValueChanged.AddListener(ScrollRectListener);

            if (pointingFirstArrow != null || pointingEndArrow != null)
            {
                m_ScrollRect.onValueChanged.AddListener(OnDragListener);
                OnDragListener(Vector2.zero);
            }
            
            m_isInited = true;

        }
        //检查 Anchor 是否正确
        private void CheckAnchor(RectTransform rectTrans)
        {
            if(direction == e_Direction.Vertical)
            {
                if (!((rectTrans.anchorMin == new Vector2(0, 1) && rectTrans.anchorMax == new Vector2(0, 1)) || (rectTrans.anchorMin == new Vector2(0, 1) && rectTrans.anchorMax == new Vector2(1, 1))))
                {
                    rectTrans.anchorMin = new Vector2(0, 1);
                    rectTrans.anchorMax = new Vector2(1, 1);
                }
            }
            else
            {
                if (!((rectTrans.anchorMin == new Vector2(0, 1) && rectTrans.anchorMax == new Vector2(0, 1)) ||
                         (rectTrans.anchorMin == new Vector2(0, 0) && rectTrans.anchorMax == new Vector2(0, 1))))
                {
                    rectTrans.anchorMin = new Vector2(0, 0);
                    rectTrans.anchorMax = new Vector2(0, 1);
                }
            }
        }

        //实时刷新列表时用
        public virtual void UpdateList()
        {
            for (int i = 0, length = m_CellInfos.Length; i < length; i++)
            {
                CellInfo cellInfo = m_CellInfos[i];
                if (cellInfo.obj != null)
                {
                    float rangePos = direction == e_Direction.Vertical ? cellInfo.pos.y : cellInfo.pos.x;
                    if (!IsOutRange(rangePos))
                    {
                        FuncCallBack?.Invoke(cellInfo.obj,cellInfo.obj.index);
                    }
                }
            }
        }

        //刷新某一项
        public void UpdateCell(int index)
        {
            CellInfo cellInfo = m_CellInfos[index];
            if (cellInfo.obj != null)
            {
                float rangePos = direction == e_Direction.Vertical ? cellInfo.pos.y : cellInfo.pos.x;
                if (!IsOutRange(rangePos))
                {
                    FuncCallBack?.Invoke(cellInfo.obj,cellInfo.obj.index);
                }
            }
        }

        public void MoveToIndex(int index)
        {
            if (direction == e_Direction.Vertical)
            {
                float moveDelta = (spacing + m_CellObjectHeight) * Mathf.CeilToInt((float)index / row) + m_CellOriginPos.y;
                m_ContentRectTrans.anchoredPosition = new Vector2(m_ContentRectTrans.anchoredPosition.x, moveDelta);
            }
            else
            {
                float moveDelta = (spacing + m_CellObjectWidth) * Mathf.CeilToInt((float)index / row) + m_CellOriginPos.x;
                m_ContentRectTrans.anchoredPosition = new Vector2(-moveDelta, m_ContentRectTrans.anchoredPosition.y);
            }
        }
        
        public virtual void ShowList(int num)
        {
            m_MinIndex = -1;
            m_MaxIndex = -1;

            //-> 计算 Content 尺寸
            if(direction == e_Direction.Vertical)
            {
                float contentSize = (spacing + m_CellObjectHeight) * Mathf.CeilToInt((float)num / row) + m_CellOriginPos.y;
                m_ContentHeight = contentSize;
                m_ContentWidth = m_ContentRectTrans.sizeDelta.x;
                contentSize = contentSize < m_ScrollRectTrans.rect.height ? m_ScrollRectTrans.rect.height : contentSize;
                m_ContentRectTrans.sizeDelta = new Vector2(m_ContentWidth, contentSize);
                if (num != m_MaxCount)
                {
                    m_ContentRectTrans.anchoredPosition = new Vector2(m_ContentRectTrans.anchoredPosition.x, 0);
                }
            }
            else
            {
                float contentSize = (spacing + m_CellObjectWidth) * Mathf.CeilToInt((float)num / row) + m_CellOriginPos.x;
                m_ContentWidth = contentSize;
                m_ContentHeight = m_ContentRectTrans.sizeDelta.x;
                contentSize = contentSize < m_ScrollRectTrans.rect.width ? m_ScrollRectTrans.rect.width : contentSize;
                m_ContentRectTrans.sizeDelta = new Vector2(contentSize, m_ContentHeight);
                if (num != m_MaxCount)
                {
                    m_ContentRectTrans.anchoredPosition = new Vector2(0, m_ContentRectTrans.anchoredPosition.y);
                }
            }

            //-> 计算 开始索引
            int lastEndIndex = 0;

            //-> 过多的物体 扔到对象池 ( 首次调 ShowList函数时 则无效 )
            if (m_isSetCount)
            {
                lastEndIndex = num > m_MaxCount ? m_MaxCount : num;
                lastEndIndex = m_IsClearList ? 0 : lastEndIndex;

                int count = m_IsClearList ? m_CellInfos.Length : m_MaxCount;
                for (int i = lastEndIndex; i < count; i++)
                {
                    if (m_CellInfos[i].obj != null)
                    {
                        SetPoolsObj(m_CellInfos[i].obj);
                        m_CellInfos[i].obj = null;
                    }
                }
            }
            
            CellInfo[] tempCellInfos = m_CellInfos;
            m_CellInfos = new CellInfo[num];

            //-> 1: 计算 每个Cell坐标并存储 2: 显示范围内的 Cell
            for (int i = 0; i < num; i++)
            {
                // * -> 存储 已有的数据 ( 首次调 ShowList函数时 则无效 )
                if (m_MaxCount != -1 && i < lastEndIndex)
                {
                    CellInfo tempCellInfo = tempCellInfos[i];
                    //-> 计算是否超出范围
                    float rPos = direction == e_Direction.Vertical ? tempCellInfo.pos.y : tempCellInfo.pos.x;
                    if (!IsOutRange(rPos))
                    {
                        //-> 记录显示范围中的 首位index 和 末尾index
                        m_MinIndex = m_MinIndex == -1 ? i : m_MinIndex; //首位index
                        m_MaxIndex = i; // 末尾index

                        if (tempCellInfo.obj == null)
                        {
                            tempCellInfo.obj = GetPoolsObj();
                        }
                        tempCellInfo.obj.transform.GetComponent<RectTransform>().anchoredPosition = tempCellInfo.pos;
                        tempCellInfo.obj.index = i;
                        tempCellInfo.obj.gameObject.SetActive(true);
                        
                        FuncCallBack?.Invoke(tempCellInfo.obj,tempCellInfo.obj.index);
                    }
                    else
                    {
                        SetPoolsObj(tempCellInfo.obj);
                        tempCellInfo.obj = null;
                    }
                    m_CellInfos[i] = tempCellInfo;
                    continue;
                }

                CellInfo cellInfo = new CellInfo();

                float pos = 0;  //坐标( isVertical ? 记录Y : 记录X )
                float rowPos = 0; //计算每排里面的cell 坐标

                // * -> 计算每个Cell坐标
                if(direction == e_Direction.Vertical)
                {
                    pos = m_CellObjectHeight * Mathf.FloorToInt(i / row) + spacing * Mathf.FloorToInt(i / row);
                    
                    if (row>1 && spacingVer>-1f)
                    {
                        rowPos = m_CellObjectWidth * (i % row) + spacingVer * (i % row);
                    }
                    else
                    {
                        rowPos = m_CellObjectWidth * (i % row) + spacing * (i % row);
                    }
                    cellInfo.pos = new Vector2(rowPos, -pos) + m_CellOriginPos;
                }
                else
                {
                    pos = m_CellObjectWidth * Mathf.FloorToInt(i / row) + spacing * Mathf.FloorToInt(i / row);
                    
                    if (row>1 && spacingVer>-1f)
                    {
                        rowPos = m_CellObjectHeight * (i % row) + spacingVer * (i % row);
                    }
                    else
                    {
                        rowPos = m_CellObjectHeight * (i % row) + spacing * (i % row);
                    }
                    cellInfo.pos = new Vector2(pos, -rowPos) + m_CellOriginPos;
                }

                //-> 计算是否超出范围
                float cellPos = direction == e_Direction.Vertical ? cellInfo.pos.y : cellInfo.pos.x;
                if (IsOutRange(cellPos))
                {
                    cellInfo.obj = null;
                    m_CellInfos[i] = cellInfo;
                    continue;
                }

                //-> 记录显示范围中的 首位index 和 末尾index
                m_MinIndex = m_MinIndex == -1 ? i : m_MinIndex; //首位index
                m_MaxIndex = i; // 末尾index

                //-> 取或创建 Cell
                ScrollViewCell cell = GetPoolsObj();
                cell.transform.GetComponent<RectTransform>().anchoredPosition = cellInfo.pos;
                cell.index = i;

                //-> 存数据
                cellInfo.obj = cell;
                m_CellInfos[i] = cellInfo;

                //-> 回调  函数
                FuncCallBack?.Invoke(cell,cell.index);
            }

            m_MaxCount = num;
            m_isSetCount = true;

            if (pointingFirstArrow != null || pointingEndArrow != null)
            {
                OnDragListener(Vector2.zero);
            }
        }

        // 更新滚动区域的大小
        public void UpdateSize()
        {
            Rect rect = GetComponent<RectTransform>().rect;
            m_PlaneHeight = rect.height;
            m_PlaneWidth = rect.width;
        }

        //滑动事件
        protected virtual void ScrollRectListener(Vector2 value)
        {
            UpdateCheck();
        }

        private void UpdateCheck()
        {
            if (m_CellInfos == null)
                return;
            //两个循环，是避免放回对象池和取出同步操作，导致取出找不到对象而生成多个
            //把超出范围的cell 扔进 poolsObj里
            for (int i = 0; i < m_CellInfos.Length; i++)
            {
                float rangePos = direction == e_Direction.Vertical ? m_CellInfos[i].pos.y : m_CellInfos[i].pos.x;
                if (IsOutRange(rangePos))
                {
                    if (m_CellInfos[i].obj != null)
                    {
                        SetPoolsObj(m_CellInfos[i].obj);
                        m_CellInfos[i].obj = null;
                    }
                }
            }
            //显示范围内的cell
            for (int i = 0; i < m_CellInfos.Length; i++)
            {
                float rangePos = direction == e_Direction.Vertical ? m_CellInfos[i].pos.y : m_CellInfos[i].pos.x;
                if (!IsOutRange(rangePos)) 
                {
                    if (m_CellInfos[i].obj == null)
                    {
                        ScrollViewCell cell = GetPoolsObj();
                        cell.transform.localPosition = m_CellInfos[i].pos;
                        cell.index = i;
                        m_CellInfos[i].obj = cell;
                        
                        FuncCallBack?.Invoke(cell,cell.index);
                    }
                }
            }
        }

        //判断是否超出显示范围
        protected bool IsOutRange(float pos)
        {
            Vector3 listP = m_ContentRectTrans.anchoredPosition;
            if(direction == e_Direction.Vertical)
            {
                if (pos + listP.y > m_CellObjectHeight || pos + listP.y < -m_ScrollRectTrans.rect.height)
                {
                    return true;
                }
            }
            else
            {
                if (pos + listP.x < -m_CellObjectWidth || pos + listP.x > m_ScrollRectTrans.rect.width)
                {
                    return true;
                }
            }
            return false;
        }

        //对象池 机制  (存入， 取出) cell
        protected Stack<ScrollViewCell> poolsObj = new Stack<ScrollViewCell>();
        //取出 cell
        protected virtual ScrollViewCell GetPoolsObj()
        {
            ScrollViewCell cell = null;
            if (poolsObj.Count > 0)
            {
                cell = poolsObj.Pop();
            }

            if (cell == null)
            {
                cell = Instantiate(scrollViewCell,m_Content.transform,false) as ScrollViewCell;
            }
            cell.transform.localScale = Vector3.one;
            cell.gameObject.SetActive(true);
     
            return cell;
        }
        //存入 cell
        protected virtual void SetPoolsObj(ScrollViewCell cell)
        {
            if (cell != null)
            {
                cell.gameObject.SetActive(false);
                poolsObj.Push(cell);
            }
        }
        
        public void DisposeAll()
        {
            if (FuncCallBack != null)
            {
                FuncCallBack = null;
            }
            if (FuncOnClickCallBack != null)
            {
                FuncOnClickCallBack = null;
            }
        }

        protected void OnDestroy()
        {
            DisposeAll();
        }

        public virtual void OnClickCell(ScrollViewCell cell) { }

        //-> ExpandCircularScrollView 函数
        public virtual void OnClickExpand(int index) { }

        //-> FlipCircularScrollView 函数
        public virtual void SetToPageIndex(int index) { }

        public virtual void OnBeginDrag(PointerEventData eventData)
        {

        }

        public void OnDrag(PointerEventData eventData)
        {
        }

        public virtual void OnEndDrag(PointerEventData eventData)
        {

        }

        protected void OnDragListener(Vector2 value)
        {
            float normalizedPos = direction == e_Direction.Vertical ? m_ScrollRect.verticalNormalizedPosition : m_ScrollRect.horizontalNormalizedPosition;
            if(direction == e_Direction.Vertical)
            {
                if (m_ContentHeight - m_ScrollRectTrans.rect.height < 10)
                {
                    pointingFirstArrow.SetActive(false);
                    pointingEndArrow.SetActive(false);
                    return;
                }
            }
            else
            {
                if (m_ContentWidth - m_ScrollRectTrans.rect.width < 10)
                {
                    pointingFirstArrow.SetActive(false);
                    pointingEndArrow.SetActive(false);
                    return;
                }
            }

            if (normalizedPos >= 0.9)
            {
                if (direction == e_Direction.Vertical)
                {
                    pointingFirstArrow.SetActive(false);
                    pointingEndArrow.SetActive(true);
                }
                else
                {
                    pointingFirstArrow.SetActive(true);
                    pointingEndArrow.SetActive(false);
                }
            }
            else if (normalizedPos <= 0.1)
            {
                if (direction == e_Direction.Vertical)
                {
                    pointingFirstArrow.SetActive(true);
                    pointingEndArrow.SetActive(false);
                }
                else
                {
                    pointingFirstArrow.SetActive(false);
                    pointingEndArrow.SetActive(true);
                }
            }
            else
            {
                pointingFirstArrow.SetActive(true);
                pointingEndArrow.SetActive(true);
            }

        }
    }
}